from __future__ import annotations

from collections.abc import Mapping, MutableMapping, Sequence
from typing import Any

from sentry.integrations.slack.client import SlackClient
from sentry.models.integrations.integration import Integration
from sentry.models.organization import Organization
from sentry.models.user import User
from sentry.services.hybrid_cloud.integration import RpcIntegration
from sentry.services.hybrid_cloud.organization import RpcOrganization
from sentry.shared_integrations.exceptions import ApiError

from ..utils import logger

SLACK_GET_USERS_PAGE_LIMIT = 100
SLACK_GET_USERS_PAGE_SIZE = 200


def get_users(integration: Integration, organization: Organization) -> Sequence[Mapping[str, Any]]:
    client = SlackClient(integration_id=integration.id)

    user_list = []
    next_cursor = None
    for i in range(SLACK_GET_USERS_PAGE_LIMIT):
        try:
            next_users = client.get(
                "/users.list",
                params={"limit": SLACK_GET_USERS_PAGE_SIZE, "cursor": next_cursor},
            )
        except ApiError as e:
            logger.info(
                "post_install.fail.slack_users.list",
                extra={
                    "error": str(e),
                    "organization": organization.slug,
                    "integration_id": integration.id,
                },
            )
            break
        user_list += next_users["members"]

        next_cursor = next_users["response_metadata"]["next_cursor"]
        if not next_cursor:
            break

    return user_list


def get_slack_info_by_email(
    integration: Integration, organization: Organization
) -> Mapping[str, Mapping[str, str]]:
    return {
        member["profile"]["email"]: {
            "email": member["profile"]["email"],
            "team_id": member["team_id"],
            "slack_id": member["id"],
        }
        for member in get_users(integration, organization)
        if not member["deleted"] and member["profile"].get("email")
    }


def get_slack_data_by_user(
    integration: Integration | RpcIntegration,
    organization: Organization | RpcOrganization,
    emails_by_user: Mapping[User, Sequence[str]],
) -> Mapping[User, Mapping[str, str]]:
    slack_info_by_email = get_slack_info_by_email(integration, organization)
    slack_data_by_user: MutableMapping[User, Mapping[str, str]] = {}
    for user, emails in emails_by_user.items():
        for email in emails:
            if email in slack_info_by_email:
                slack_data_by_user[user] = slack_info_by_email[email]
                break
    return slack_data_by_user
